using System;
using System.Xml;
using System.IO;
using System.Text;
using System.Text.RegularExpressions;
using System.Collections;
using System.Collections.Generic;
using System.Xml.Serialization;
using rlTodayMakeSkin.Project.XmlElement;


namespace rlTodayMakeSkin.Project
{

    /// <summary>
    /// Value of skin width.
    /// </summary>
    public enum SkinWidth
    {
        VGA = 640,
        QVGA = 320
    };


    /// <summary>
    /// Represents a rlToday skin project. Contains name, path and elements
    /// contained in the skin.xml
    /// </summary>
    [XmlRoot("today")]
    public class rlTodayProject
    {
        private rlTodayXmlElementCollection skinElements=new rlTodayXmlElementCollection();
        private ISkinDisplay skinDisplay;
        private string nameProject;
        private string pathProject;
        private int intRefresh=100;
        private int intHeight=480;
        private int intWidth = (int)SkinWidth.VGA; //begin with 640
        private bool modified;

        private const string NAME_ELS_COLLECTION = "els";

        public rlTodayProject(string pathProject, ISkinDisplay display)
        {
            this.nameProject = pathProject.Substring(pathProject.LastIndexOf('\\') + 1);
            this.pathProject = pathProject;
            if (Directory.Exists(pathProject) == false)
            {
                Directory.CreateDirectory(pathProject);
                //Directory.CreateDirectory(pathProject + "\\Icons");
            }
            skinDisplay = display;
        }

        public rlTodayProject() { } //for the serialization


        /// <summary>
        /// Gets the name of this skin project (name of the folder that contains skin.xml file)
        /// </summary>
        [XmlIgnore()]
        public string Name
        {
            get { return nameProject; }
        }

        /// <summary>
        /// Gets, sets the path of this skin project (included name of the project folder)
        /// </summary>
        [XmlIgnore()]
        public string Path
        {
            get { return pathProject; }
            set { 
                pathProject = value;
                nameProject = pathProject.Substring(pathProject.LastIndexOf('\\') + 1);
            }
        }

        /// <summary>
        /// add a xml element to this project and adds it
        /// to the skin display
        /// </summary>
        /// <param name="el">element to be add to this project</param>
        /// <param name="blnSelected">true if the element must be selected in the skin</param>
        public void addElement(rlTodayXmlElement el, bool blnSelected){
            skinElements.Add(el);
            skinDisplay.addSkinElement(el, blnSelected);
            modified = true;
        }

        /// <summary>
        /// Removes an element from this project
        /// </summary>
        /// <param name="el">object to remove</param>
        public void deleteElement(rlTodayXmlElement el)
        {
            skinElements.remove(el);
            modified = true;
        }

        /// <summary>
        /// Returns a rlTodayXmlElementCollection that contains all element
        /// into the project
        /// </summary>
        [XmlArray(ElementName=NAME_ELS_COLLECTION),
        XmlArrayItem("Text", Type = typeof(rlText)),
        XmlArrayItem("Registry", Type = typeof(rlRegistry)),
        XmlArrayItem("Image", Type = typeof(rlImage)),
        XmlArrayItem("DigitalClock", Type = typeof(rlDigitalClock)),
        XmlArrayItem("Date", Type = typeof(rlDate)),
        XmlArrayItem("CalendarHeader", Type = typeof(rlCalendarHeader)),
        XmlArrayItem("Calendar", Type = typeof(rlCalendar)),
        XmlArrayItem("AppName", Type = typeof(rlAppName)),
        XmlArrayItem("Application", Type = typeof(rlApplication)),
        XmlArrayItem("AnalogClock", Type = typeof(rlAnalogClock))]
        public rlTodayXmlElementCollection Elements{
            get {return skinElements; }
        }

        
        /// <summary>
        /// Sets, gets refresh attribute for skin
        /// </summary>
        [XmlAttribute("refresh")]
        public int Refresh
        {
            get { return intRefresh; }
            set { intRefresh = value; }
        }

        /// <summary>
        /// Sets, gets the height of the rlToday surface
        /// </summary>
        [XmlAttribute("height")]
        public int Height
        {
            get { return intHeight; }
            set { intHeight = value; skinDisplay.Height = value; }
        }

        /// <summary>
        /// Returns the skin width in a integer value
        /// </summary>
        /// <remarks>
        ///     this attribute will be probably ignored by rlToday but is useful
        ///     to automatically manage the width of the skin.
        ///</remarks>
        [XmlAttribute("width")]
        public int Width
        {
            get { return intWidth; }
            set
            {
                intWidth = value;
                skinDisplay.Width = intWidth;
            }
        }


        /// <summary>
        /// Writes in skin.xml file the content of xml elments
        /// </summary>
        public void save()
        {
            //leggi questo per serializzare bene la collezione:
            //guide to seralization:
            //http://msdn.microsoft.com/en-us/library/system.xml.serialization.xmlattributeoverrides.aspx
            
            string pathSkinFile = pathProject + "\\skin.xml";
            StringWriter sw = new StringWriter(new StringBuilder());
            string toWriter = "";
            XmlSerializer salva = new XmlSerializer(typeof(rlTodayProject));//, attrOverrides);
            StreamWriter stW = null;
            try
            {
                stW = new StreamWriter(new FileStream(pathSkinFile, FileMode.Create, FileAccess.Write), Encoding.UTF8);
                XmlWriterSettings settings = new XmlWriterSettings();
                settings.Encoding = Encoding.UTF8;
                settings.Indent = true;
                settings.IndentChars = "\t";
                settings.NewLineChars = Environment.NewLine;
                settings.NewLineHandling = NewLineHandling.None;
                settings.ConformanceLevel = ConformanceLevel.Document;
                XmlWriter writer = XmlTextWriter.Create(sw, settings);
                salva.Serialize(writer, this);
                toWriter = sw.GetStringBuilder().ToString();
                toWriter = Regex.Replace(toWriter, "utf-16", "utf-8");
                toWriter = Regex.Replace(toWriter, @"(<(\/)?" + NAME_ELS_COLLECTION + @">|xmlns[\S]*)", "");//clears xml of useless elements
            }
            catch (Exception ex)
            {
                throw ex;
            }
            finally
            {
                if(toWriter != "" && stW != null){
                    stW.Write(toWriter);
                    stW.Flush();
                    stW.Close();
                }
                sw.Close();
            }
            modified = false;
        }

        [XmlIgnore()]
        public bool IsModified
        {
            get { return modified; }
            set { modified = value;}
        }

        /// <summary>
        /// Loads a skin.xml file
        /// </summary>
        /// <param name="xmlSkinPath">path of a folder that must contain skin.xml file</param>
        public void loadSkin(string xmlSkinPath)
        {
            pathProject = xmlSkinPath;
            nameProject = pathProject.Substring(pathProject.LastIndexOf('\\') + 1);
            readXmlDocument(xmlSkinPath + "\\skin.xml");
            skinDisplay.drawAll();
            modified = false;
        }

        /// <summary>
        /// Returns skin display associated whit this project
        /// </summary>
        /// <returns></returns>
        public ISkinDisplay getSkinDisplay()
        {
            return skinDisplay;
        }

        private void readXmlDocument(string xmlSkinPath)
        /*Read the skin.xml file and shows each its control
         in pnlSking*/
        {
            XmlDocument xd = new XmlDocument();
            XmlNode xnToday = null;
            xd.Load(new StreamReader(xmlSkinPath, true));
            if (xd.ChildNodes.Count > 1)
                //means that there's <?xml..element in the file
                xnToday = xd.ChildNodes[1];
            else
                xnToday = xd.ChildNodes[0];

            setTodayAttributes(xnToday);
            rlTodayXmlElement el = null;
            foreach (XmlNode xn in xnToday.ChildNodes)
            {
                el = getNewElement(xn);
                if (el == null) continue;
                addElement(el, false);
                //                skinDisplay.addSkinElement(el, false);
            }


        }

        /// <summary>
        /// Returns a rlTodayXmlElement created by this XmlNode
        /// </summary>
        /// <param name="xn">XmlNode that contains information of rlTodayXmlElement</param>
        /// <returns>a new rlTodayXmlElement</returns>
        public rlTodayXmlElement getNewElement(XmlNode xn)
        {
            rlTodayXmlElement el = null;
            switch (xn.Name)
            {
                case "AnalogClock":
                    el = new rlAnalogClock(xn.Attributes, this);
                    break;
                case "Calendar":
                    el = new rlCalendar(xn, this);
                    break;
                case "AppName":
                    el = new rlAppName(xn, this);
                    break;
                case "Application":
                    el = new rlApplication(xn.Attributes, this);
                    break;
                case "DigitalClock":
                    el = new rlDigitalClock(xn, this);
                    break;
                case "CalendarHeader":
                    el = new rlCalendarHeader(xn, this);
                    break;
                case "Date":
                    el = new rlDate(xn, this);
                    break;
                case "Image":
                    el = new rlImage(xn.Attributes, this);
                    break;
                case "Registry":
                    el = new rlRegistry(xn, this);
                    break;
                case "Text":
                    el = new rlText(xn, this);
                    break;
                case "#comment": //comments are ignored
                    el = null;
                    break;
                default:
                    el = new UnknownXmlElement(xn.Attributes, this, xn.Name);
                    break;
            }
            return el;
        }

        /// <summary>
        /// Returns an empty rlTodayXmlElement
        /// </summary>
        /// <param name="name">Name of the elment to create</param>
        /// <returns>a new rlTodayXmlElement</returns>
        public rlTodayXmlElement getNewElement(string name)
        {
            rlTodayXmlElement el = null;
            switch (name)
            {
                case "AnalogClock":
                    el = new rlAnalogClock(this);
                    break;
                case "Calendar":
                    el = new rlCalendar(this);
                    break;
                case "AppName":
                    el = new rlAppName(this);
                    break;
                case "Application":
                    el = new rlApplication(this);
                    break;
                case "DigitalClock":
                    el = new rlDigitalClock(this);
                    break;
                case "CalendarHeader":
                    el = new rlCalendarHeader(this);
                    break;
                case "Date":
                    el = new rlDate(this);
                    break;
                case "Registry":
                    el = new rlRegistry(this);
                    break;
                case "Text":
                    el = new rlText(this);
                    break;
                case "Image":
                    el = new rlImage(this);
                    break;
            }
            return el;
        }

        private void setTodayAttributes(XmlNode todayNode)
        {
            XmlAttributeCollection today = todayNode.Attributes;
            if (today["refresh"] != null) intRefresh = int.Parse(today["refresh"].Value);
            if (today["height"] != null) intHeight = int.Parse(today["height"].Value);
            if (today["width"] != null) intWidth = int.Parse(today["width"].Value);
            //if (today["width"] != null) intWidth = int.Parse(today["width"].Value);
            skinDisplay.Height = intHeight;
            skinDisplay.Width = intWidth;
        }


    }
}
